﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Itau.SC.EnterpriseLib.Data;
using System.Xml;
using System.Xml.Linq;
using System.IO;

namespace DataAccessMock
{
    public class XmlTestCaseHandler : XmlHandler
    {
        public DataSet SearchTestCaseDataSet(string commandText, IList<ParameterInWrapper> parameter) 
        {
            XmlDocument xml = SearchXMLFile(commandText);

            List<XmlDocument> casosDeTestesSeparados = SplitTestCase(xml);

            XmlNode saida = SearchTestCase(casosDeTestesSeparados, parameter);

            DataSet ds = ReturnDataSetOutput(saida, commandText);

            return ds;
        }

        public int SearchTestCaseRowsAffected(string commandText, IList<ParameterInWrapper> parameters)
        {
            XmlDocument xml = SearchXMLFile(commandText);

            List<XmlDocument> casosDeTestesSeparados = SplitTestCase(xml);

            XmlNode saida = SearchTestCase(casosDeTestesSeparados, parameters);

            return ReturnRowsAffected(saida);
        }

        public int SearchTestCaseParameterOut(string commandText, IList<ParameterInWrapper> parametersIn, IList<ParameterOutWrapper> parametersOut)
        {
            XmlDocument xml = SearchXMLFile(commandText);

            List<XmlDocument> casosDeTestesSeparados = SplitTestCase(xml);

            XmlNode saida = SearchTestCase(casosDeTestesSeparados, parametersIn);

            return ReturnParametersOut(saida, parametersOut);
        }

        public object SearchTestCaseResult(string commandText, IList<ParameterInWrapper> parameter)
        {
            XmlDocument xml = SearchXMLFile(commandText);

            List<XmlDocument> casosDeTestesSeparados = SplitTestCase(xml);

            XmlNode saida = SearchTestCase(casosDeTestesSeparados, parameter);

            return ReturnResultOutput(saida);;
        }

        public IDataReader SearchTestCaseDataReader(string commandText, IList<ParameterInWrapper> parameter)
        {
            XmlDocument xml = SearchXMLFile(commandText);

            List<XmlDocument> casosDeTestesSeparados = SplitTestCase(xml);

            XmlNode saida = SearchTestCase(casosDeTestesSeparados, parameter);

            DataSet ds = ReturnDataSetOutput(saida, commandText);

            return new DataReaderMock(ds);
        }

        private DataSet ReturnDataSetOutput(XmlNode saida, string tableName)
        {
            DataSet ds = new DataSet();

            if (saida != null)
            {
                ds.Tables.Add(new DataTable(tableName));

                XmlNodeList cabecalho = saida.SelectNodes("Resultado/Colunas/Coluna");

                foreach (XmlNode node in cabecalho)
                {
                    ds.Tables[0].Columns.Add(node.Attributes["nome"].Value, Type.GetType(node.Attributes["tipo"].Value));
                }

                XmlNodeList dados = saida.SelectNodes("Resultado/Dados/Linhas/Linha");

                foreach (XmlNode node in dados)
                {
                    XmlNodeList celulas = node.SelectNodes("Celulas/Celula");

                    DataRow dr = ds.Tables[0].NewRow();

                    foreach (XmlNode celula in celulas)
                    {
                        dr[celula.Attributes["nome"].Value] = celula.InnerText;
                    }

                    ds.Tables[0].Rows.Add(dr);
                }
            }

            return ds;
        }

        private object ReturnResultOutput(XmlNode saida)
        {
            XmlNode retorno = saida.SelectSingleNode("Resultado/Retorno");
            return retorno.InnerText;
        }

        private int ReturnRowsAffected(XmlNode saida)
        {
            XmlNode linhasAfetadas = saida.SelectSingleNode("Resultado/LinhasAfetadas");
            return Convert.ToInt32(linhasAfetadas.InnerText);
        }

        private int ReturnParametersOut(XmlNode saida, IList<ParameterOutWrapper> parametersOut)
        {
            XmlNode linhasAfetadas = saida.SelectSingleNode("Resultado/LinhasAfetadas");

            XmlNodeList parametrosOut = saida.SelectNodes("Resultado/Parametros/Parametro");

            foreach (XmlNode parametroOut in parametrosOut)
            {
                ParameterOutWrapper parametro = null;

                try
                {
                    parametro = parametersOut.First(x => x.ParameterName.Equals(parametroOut.Attributes["nome"].Value));
                }
                catch(Exception)
                {
                    throw new Exception("Parâmetros de saída não coincidem.");
                }

                parametro.ParameterValue = parametroOut.Attributes["valor"].Value;

            }

            return Convert.ToInt32(linhasAfetadas.InnerText);
        }

        private XmlNode SearchTestCase(IList<XmlDocument> casosDeTestesSeparados, IList<ParameterInWrapper> parametrosEntrada)
        {
            XmlNode saida = null;
            bool encontrou = false;

            if (parametrosEntrada != null && parametrosEntrada.Count > 0)
            {
                foreach (XmlDocument casoDeTesteSeparado in casosDeTestesSeparados)
                {
                    IEnumerable<XmlNode> lista = casoDeTesteSeparado.SelectNodes("Teste/Entrada/Parametros/Parametro").Cast<XmlNode>();

                    var resultado = from x in lista
                                    from n in parametrosEntrada
                                    where (x.Attributes["nome"].Value == n.ParameterName.ToString()
                                    && x.Attributes["valor"].Value == n.ParameterValue.ToString())
                                    select x;

                    if (resultado.Count() == parametrosEntrada.Count)
                    {
                        saida = resultado.First<XmlNode>().ParentNode.ParentNode.ParentNode.SelectSingleNode("Saida");
                        encontrou = true;
                        break;
                    }
                }

                //Verifica se não foi encontrado
                if(!encontrou)
                    throw new Exception("Não foram encontrados casos de teste que coincidam com os parâmetros passados.");
            }
            else 
            {
                if (casosDeTestesSeparados.Count == 1)
                {
                    saida = casosDeTestesSeparados[0].SelectSingleNode("Teste/Saida");
                }
                else 
                {
                    throw new Exception("Procedure sem parâmetros, não aceita mais que um caso de teste");
                }
            }

            return saida;
        }

        private List<XmlDocument> SplitTestCase(XmlDocument xml)
        {
            XmlNodeList casosDeTestes = xml.SelectNodes("Testes/Teste");
            List<XmlDocument> casosDeTestesSeparados = new List<XmlDocument>();

            foreach (XmlNode casoDeTeste in casosDeTestes)
            {
                XmlDocument casoDeTesteSeparado = new XmlDocument();
                casoDeTesteSeparado.Load(new StringReader(casoDeTeste.OuterXml));
                casosDeTestesSeparados.Add(casoDeTesteSeparado);
            }

            return casosDeTestesSeparados;
        }
    }
}
